/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpe.cin.dmo4.middleware.server;

import br.ufpe.cin.dmo4.middleware.message.Marshaller;
import br.ufpe.cin.dmo4.middleware.message.RequestMessage;
import br.ufpe.cin.dmo4.middleware.message.ResponseMessage;
import br.ufpe.cin.dmo4.middleware.remotingerrors.RemotingError;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Danilo
 */
public class Invoker implements ServerHandlerListener {

    private Marshaller marshaller;

    private Map<String, Object> lookup;

    public Invoker(Marshaller marshaller) {
        this.marshaller = marshaller;
        lookup = new HashMap<>();
    }
    
    public void registerObject(String key, Object obj){
        lookup.put(key, obj);
    }

    @Override
    public void receiveMessage(byte[] messageArray, ClientConnection conn) {
        try {
            RequestMessage request = null;
            try {
                request = marshaller.unmarshalRequest(messageArray);
            } catch (Exception e) {                
                e.printStackTrace();
            }

            Object response = invoke(request);

            ResponseMessage responseMessage = new ResponseMessage(request.getRequestId(), 0, response);
            
            byte[] responseBytes = marshaller.marshall(responseMessage);
            
            conn.send(responseBytes);
        } catch (RemotingError e) {
            ResponseMessage response = new ResponseMessage(-1, -1, null);
            response.setError(e);

            byte[] error = marshaller.marshall(response);

            conn.send(error);
        } catch (Exception e) {
            ResponseMessage response = new ResponseMessage(-1, -1, null);
            response.setError(new RemotingError());

            byte[] error = marshaller.marshall(response);

            conn.send(error);
        }

    }

    private Object invoke(RequestMessage request) throws RemotingError {
        try {
            Object remoteObject = lookup.get(request.getObjectKey());            
            Object[] parameters = request.getBody().toArray();            
            Class[] classTypes = new Class[parameters.length];
            
            for (int i = 0; i < parameters.length; i++) {
                classTypes[i] = parameters[i].getClass();
            }
            
            Class remoteObjClass = remoteObject.getClass();
            Method method = remoteObjClass.getDeclaredMethod(request.getOperation(), classTypes);
            
            Object response = method.invoke(remoteObject, parameters);
            
            return response;
        } catch (Exception exception) {
            exception.printStackTrace();
            throw new RemotingError();
        }
    }

}
